const chokidar = require('chokidar');
const bodyParser = require('body-parser');
const chalk = require('chalk');
const path = require('path');

const mockDir = path.join(process.cwd(), 'mocks');
/**
 * 通过express 路由方式注册mock api
 * @param {*} app express实例
 * @returns {object}
 * mockLastIndex mock路由开始位置索引
 * mockRoutesLength 路由长度mocks api
 */
function registerRoutes(app) {
  let mockLastIndex;
  // 读取注册路由
  const { default: mocks } = require('./index.js');
  console.log('\n Mocks Server Register Routes : \n');
  for (const mock  of mocks) {
    let url = mock.url ? mock.url.source || mock.url : '';
    url = url.replace(/\\\//g, '/');
    console.log(' -' + mock.type + ' - ' + url);
    // 注册路由
    app[mock.type](mock.url, mock.response);
  }
  mockLastIndex = app._router.stack.length;
  const mockRoutesLength = Object.keys(mocks).length;
  return {
    mockStartIndex: mockLastIndex - mockRoutesLength,
    mockRoutesLength: Object.keys(mocks).length
  }
}

// 清除/mocks目录require缓存
/**
 * require.cache 是 Node.js 中的一个内置对象，用于缓存通过 require() 函数加载过的模块，避免同一模块被重复加载和执行，以提升性能
 * 缓存机制：当首次 require('模块路径') 时，Node.js 会执行模块代码并将结果（模块的 exports 对象）存入 require.cache，键为模块的绝对路径，值为模块对象
 * 删除缓存：delete require.cache[require.resolve('模块路径')]，后续再 require 该模块时会重新执行代码
 * 查看缓存：直接打印 require.cache 可查看当前已缓存的所有模块信息
 * 注意事项：仅在 Node.js 环境生效，浏览器端 JavaScript 无此对象；修改缓存可能导致模块状态不一致，需谨慎使用
 */
function unRegisterRoutes() {
  Object.keys(require.cache).forEach(i => {
    // 清除/mocks目录require缓存
    if (i.includes(mockDir)) {
      delete require.cache[require.resolve(i)];
    }
  });
}

module.exports = devServer => {
  // app 为 Express 实例
  let app = devServer.app;
  // es6 polyfill(需要加下面这行来编译import导入，还有babel.config.js加@babel/preset-env预设)
  require('@babel/register');

  // Mock启用，则在开发模式添加 mock api 拦截
  // 此处必须是字符串方式判断，因其不在浏览器当中
  if (process.env.VUE_APP_MOCK_ENABLE !== 'true') {
    return;
  }
  // 开发模式，没有统一前缀，添加自定义请求头，区分是mock服务
  // 通过 app.all('*', ...) 为所有路由和 HTTP 方法添加一个中间件，在响应头中加入标识，可能用于标识这是一个模拟 API
  app.all('*', function(req, res, next) {
    res.header('X-Mock-Api', 'xyMock');
    next();
  });
  // 使用 bodyParser.json() 解析 Content-Type 为 application/json 的请求体
  app.use(bodyParser.json());
  // 使用 bodyParser.urlencoded() 解析表单提交的请求体（application/x-www-form-urlencoded），extended: true 表示支持复杂的对象结构
  app.use(
    bodyParser.urlencoded({
      extended: true
    })
  );
  let lastRegReturn = registerRoutes(app);
  let mockRoutesLength = lastRegReturn.mockRoutesLength;
  let mockStartIndex = lastRegReturn.mockStartIndex;

  // 监听/mocks文件（忽略/mocks/mocks-server.js），热加载Mocks Server
  chokidar.watch(mockDir, {
    ignored: /mock-server/,
    ignoreInitial: true
  }).on('all', (event, path) => {
    if (event === 'change' || event === 'add') {
      // 移除 mock api Express 路由堆栈
      app._router.stack.splice(mockStartIndex, mockRoutesLength);
      // 清除路由文件缓存
      unRegisterRoutes();
      // 重新注册 Mocks api路由
      lastRegReturn = registerRoutes(app);
      mockRoutesLength = lastRegReturn.mockRoutesLength;
      mockStartIndex = lastRegReturn.mockStartIndex;

      console.log(chalk.magentaBright(`\n > Mocks Server hot reload success! changed ${path}`));
    }
  });
};
